package com.wg.web_app.dao;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;

public class SuperDao<T> {
	Class<T> clazz;
	String tableName;
	Field[] fields;
	String[] columnNames;

	public SuperDao(Class<T> clazz) {
		this.clazz = clazz;
		Table table = (Table) clazz.getDeclaredAnnotationsByType(Table.class)[0];
		tableName = table.name();
		fields = clazz.getDeclaredFields();
		columnNames = new String[fields.length];
		for(int i=0;i<fields.length;i++){
			Column column = fields[i].getDeclaredAnnotation(Column.class);
			columnNames[i] = column.name();
		}
	}
	
	public void print(T data){
		for(int i=0;i<fields.length;i++){
			try {
				fields[i].setAccessible(true);
				Object obj = fields[i].get(data);
				if(obj != null){
					System.out.println(obj+"=>"+columnNames[i]);
				}
			} catch (IllegalArgumentException e) {
				throw new RuntimeException(e.getMessage());
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e.getMessage());
			}
		}
		
	}
	
	public String getQuerySql(T data){
		StringBuilder sb = new StringBuilder("select * from ");
		sb.append("`");
		sb.append(tableName);
		sb.append("`");
		sb.append(" where ");
		boolean start = false;
		for(int i=0;i<fields.length;i++){
			try {
				fields[i].setAccessible(true);
				Object obj = fields[i].get(data);
				if(obj != null){
					if(!start){
						start = true;
					}else{
						sb.append(" and ");
					}
					sb.append(columnNames[i]);
					sb.append(" = ");
					sb.append("?");
				}
			} catch (IllegalArgumentException e) {
				throw new RuntimeException(e.getMessage());
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e.getMessage());
			}
		}
		sb.append(";");
		return sb.toString();
	}
	
	public String getInsertSql(T data){
		StringBuilder sb = new StringBuilder("insert into ");
		sb.append("`");
		sb.append(tableName);
		sb.append("` ");
		StringBuilder sbColumns = new StringBuilder();
		StringBuilder sbValues = new StringBuilder();
		boolean first = true;
		for(int i=0;i<fields.length;i++){
			try {
				fields[i].setAccessible(true);
				Object obj = fields[i].get(data);
				if(obj != null){
					if(!first){
						sbColumns.append(",");
						sbValues.append(",");
					}
					else {
						first = false;
					}
					sbColumns.append(columnNames[i]);
					sbValues.append("?");
				}
			} catch (IllegalArgumentException e) {
				throw new RuntimeException(e.getMessage());
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e.getMessage());
			}
		}
		sb.append("(");
		sb.append(sbColumns);
		sb.append(")");
		sb.append(" values ");
		sb.append("(");
		sb.append(sbValues);
		sb.append(")");
		sb.append(";");
		return sb.toString();
	}
	
	public String getDeleteSql(T data){
		StringBuilder sb = new StringBuilder("delete from ");
		sb.append("`");
		sb.append(tableName);
		sb.append("`");
		sb.append(" where ");
		boolean start = false;
		for(int i=0;i<fields.length;i++){
			try {
				fields[i].setAccessible(true);
				Object obj = fields[i].get(data);
				if(obj != null){
					if(!start){
						start = true;
					}else{
						sb.append(" and ");
					}
					sb.append(columnNames[i]);
					sb.append(" = ");
					sb.append("?");
				}
			} catch (IllegalArgumentException e) {
				throw new RuntimeException(e.getMessage());
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e.getMessage());
			}
		}
		sb.append(";");
		return sb.toString();
	}
	
	
	public String getUpdateSql(T src,T dest){
		StringBuilder sb = new StringBuilder("update  ");
		sb.append("`");
		sb.append(tableName);
		sb.append("`");
		sb.append(" set ");
		boolean start = false;
		for(int i=0;i<fields.length;i++){
			try {
				fields[i].setAccessible(true);
				Object obj = fields[i].get(dest);
				if(obj != null){
					if(!start){
						start = true;
					}else{
						sb.append(" , ");
					}
					sb.append(columnNames[i]);
					sb.append(" = ");
					sb.append("?");
				}
			} catch (IllegalArgumentException e) {
				throw new RuntimeException(e.getMessage());
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e.getMessage());
			}
		}
		
		sb.append(" where ");
		
		start = false;
		for(int i=0;i<fields.length;i++){
			try {
				fields[i].setAccessible(true);
				Object obj = fields[i].get(src);
				if(obj != null){
					if(!start){
						start = true;
					}else{
						sb.append(" and ");
					}
					sb.append(columnNames[i]);
					sb.append(" = ");
					sb.append("?");
				}
			} catch (IllegalArgumentException e) {
				throw new RuntimeException(e.getMessage());
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e.getMessage());
			}
		}
		sb.append(";");
		return sb.toString();
	}
	
	public Object[] getQueryValues(T data){
		List<Object> objs = new ArrayList<Object>();
		for(int i=0;i<fields.length;i++){
			try {
				fields[i].setAccessible(true);
				Object obj = fields[i].get(data);
				if(obj != null){
					objs.add(obj);
				}
			} catch (IllegalArgumentException e) {
				throw new RuntimeException(e.getMessage());
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e.getMessage());
			}
		}
		return objs.toArray();
	}
	
	public Object[] getQueryValues(T... datas){
		List<Object> objs = new ArrayList<Object>();
		for(int i=0;i<fields.length;i++){
			for(int j=0;j<datas.length;j++){
				try {
					fields[i].setAccessible(true);
					Object obj = fields[i].get(datas[j]);
					if(obj != null){
						objs.add(obj);
					}
				} catch (IllegalArgumentException e) {
					throw new RuntimeException(e.getMessage());
				} catch (IllegalAccessException e) {
					throw new RuntimeException(e.getMessage());
				}
			}
		}
		return objs.toArray();
	}
	
	public String[] getQueryColumns(T data){
		List<String> objs = new ArrayList<String>();
		for(int i=0;i<fields.length;i++){
			try {
				fields[i].setAccessible(true);
				Object obj = fields[i].get(data);
				if(obj != null){
					objs.add(columnNames[i]);
				}
			} catch (IllegalArgumentException e) {
				throw new RuntimeException(e.getMessage());
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e.getMessage());
			}
		}
		return objs.toArray(new String[objs.size()]);
	}
	
	public T mapToObj(Map<String,Object> map){
		try {
			T t = (T) clazz.newInstance();
			for(int i=0;i<fields.length;i++){
				try {
					fields[i].setAccessible(true);
					Object value = map.get(columnNames[i]);
					fields[i].set(t, value);
				} catch (IllegalArgumentException e) {
					throw new RuntimeException(e.getMessage());
				} catch (IllegalAccessException e) {
					throw new RuntimeException(e.getMessage());
				}
			}
			return t;
		} catch (InstantiationException e) {
			throw new RuntimeException(e.getMessage());
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	public T query(T data){
		String sql = getQuerySql(data);
		System.out.println(sql);
		JdbcTemplate jdbcT = JDBCHolder.getJDBCTemplate();
		List<Map<String, Object>> list = jdbcT.queryForList(sql,getQueryValues(data));
		if(list == null || list.isEmpty())
			return null;
		return mapToObj(list.get(0));
	}

	public List<T> querys(T data) {
		String sql = getQuerySql(data);
		JdbcTemplate jdbcT = JDBCHolder.getJDBCTemplate();
		List<Map<String, Object>> list = jdbcT.queryForList(sql,getQueryValues(data));
		List<T> result = new ArrayList<T>();
		if(list == null || list.isEmpty())
			return result;
		for (Map<String, Object> t : list) {
			result.add(mapToObj(t));
		}
		return result;
	}

	public int insert(final T data) {
		final String sql = getInsertSql(data);
		System.out.println(sql);
		JdbcTemplate jdbcT = JDBCHolder.getJDBCTemplate();
		KeyHolder keyHolder = new GeneratedKeyHolder();
		try{
			jdbcT.update(new PreparedStatementCreator() {
				@Override
				public PreparedStatement createPreparedStatement(Connection paramConnection) throws SQLException {
					PreparedStatement ps  = paramConnection.prepareStatement(sql,getQueryColumns(data));
					Object [] values = getQueryValues(data);
					for(int i=0;i<values.length;i++){
						ps.setObject(i + 1, values[i]);
					}
					return ps;
				}
			},keyHolder);
			return keyHolder.getKey().intValue();
		}catch (DuplicateKeyException dke){
			return -2;
		}catch (Exception e){
			System.out.println(e.getMessage());
			return -3;			
		}
	}

	public int update(T src, T dest) {
		String sql = getUpdateSql(src, dest);
		JdbcTemplate jdbcT = JDBCHolder.getJDBCTemplate();
		Object [] destobj = getQueryValues(dest);
		Object [] srcobj = getQueryValues(src);
		Object []  ojbs = new Object[destobj.length + srcobj.length];
		int i = 0;
		for (Object object : destobj) {
			ojbs[i++] = object;
			System.out.println(object);
		}
		for (Object object : srcobj) {
			ojbs[i++] = object;
			System.out.println(object);
		}
		for (Object object : ojbs) {
			System.out.println(object);
		}
		return jdbcT.update(sql,ojbs);
	}

	public int delete(T data) {
		String sql = getDeleteSql(data);
		JdbcTemplate jdbcT = JDBCHolder.getJDBCTemplate();
		int i = jdbcT.update(sql,getQueryValues(data));
		return i;
	}

}
